import java.util.Collection;
import java.util.LinkedList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class Sucky
{
	private Position _position;
	private Direction _direction;
	private ActionType _action;
	private Sucky _precursor;
	private int _cost;
	
	public Position getPosition()
	{
		return new Position(_position.x, _position.y);
	}
	public Direction getDirection()
	{
		return _direction;
	}
	public LinkedList<String> getInstructions()
	{
		LinkedList<String> instructions = new LinkedList<String>();
		
		Sucky target = this;
		while(target != null)
		{
			instructions.addFirst(actionTypeToString(target._action));
			target = target._precursor;
		}
		
		instructions.addLast(actionTypeToString(ActionType.TurnOff));
		
		return instructions;
	}
	public Sucky(Sucky original, ActionType action)
	{
		_precursor = original;
		_action = action;
		
		_direction = original._direction;
		if(action == ActionType.TurnRight)
		{
			_direction = rotateDirectionCW(_direction);
		}
		else if(action == ActionType.TurnLeft)
		{
			_direction = rotateDirectionCCW(_direction);
		}
		
		Position offset = new Position(0,0);
		if(action == ActionType.Go)
		{
			if(_direction == Direction.North)
				offset.y = 1;
			if(_direction == Direction.East)
				offset.x = 1;
			if(_direction == Direction.South)
				offset.y = -1;
			if(_direction == Direction.West)
				offset.x = -1;
		}
		
		_position = offset.add(original._position);
		_cost = original._cost + 1;
	}
	public Sucky(Collection<String> percepts)
	{
		_action = ActionType.TurnOn;
		_cost = 0;
		Pattern perceptNamePattern = Pattern.compile("\\(\\s*([^\\s]+).*");
		for (String percept:percepts) 
		{
			Matcher perceptNameMatcher = perceptNamePattern.matcher(percept);
			if (perceptNameMatcher.matches()) 
			{
				String perceptName = perceptNameMatcher.group(1);
	
				if (perceptName.equals("HOME")) 
				{
					Matcher m = Pattern.compile("\\(\\s*HOME\\s+([0-9]+)\\s+([0-9]+)\\s*\\)").matcher(percept);
					if (m.matches()) 
					{
						int x = Integer.parseInt(m.group(1))-1, y = Integer.parseInt(m.group(2))-1;
						_position = new Position(x, y);
					}
				}
				else if (perceptName.equals("ORIENTATION"))
				{
					Matcher m = Pattern.compile("\\(\\s*(ORIENTATION)\\s+(NORTH|SOUTH|EAST|WEST)\\s*\\)").matcher(percept);
					if (m.matches()) 
					{
						String dir = m.group(2);
						if(dir.equals("NORTH")) _direction = Direction.North;
						if(dir.equals("EAST")) _direction = Direction.East;
						if(dir.equals("SOUTH")) _direction = Direction.South;
						if(dir.equals("WEST")) _direction = Direction.West;
					}
				}
				else if (perceptName.equals("AT"))
				{
					//do nothing
				}
				else if (perceptName.equals("SIZE"))
				{
					//do nothing
				}
				else 
				{
					System.out.println("other percept:" + percept);
				}
			} 
			else 
			{
				System.err.println("strange percept that does not match pattern: " + percept);
			}
		}
	}
	public Sucky forkTo(Direction dir)
	{
		if(_direction == dir)//if forward
			return this.fork(ActionType.Go);
		Direction right = rotateDirectionCW(_direction);
		if(dir == right)//if right
		{
			Sucky towardsRight = this.fork(ActionType.TurnRight);
			Sucky goRight =  towardsRight.fork(ActionType.Go);
			return goRight;
		}
		else if(dir == rotateDirectionCW(right))//if reverse
		{
			Sucky towardsRight = this.fork(ActionType.TurnRight);
			Sucky towardsBack = towardsRight.fork(ActionType.TurnRight);
			Sucky goBack =  towardsBack.fork(ActionType.Go);
			return goBack;
		}
		else//if left
		{
			Sucky towardsLeft = this.fork(ActionType.TurnLeft);
			Sucky goLeft =  towardsLeft.fork(ActionType.Go);
			return goLeft;
		}
	}
	public Sucky fork(ActionType type)
	{
		return new Sucky(this, type);
	}
	public int getCost()
	{
		return _cost;
	}
	public LinkedList<Direction> getAvailableDirections(Map map)
	{
		LinkedList<Direction> directions = new LinkedList<Direction>();
		Position north = new Position( 0, 1).add(_position);
		Position east = new Position ( 1, 0).add(_position);
		Position south = new Position( 0,-1).add(_position);
		Position west = new Position (-1, 0).add(_position);
		if(map.getAt( north ) != CellType.Wall && !map.hasVisited(north)) directions.add(Direction.North);
		if(map.getAt( east ) != CellType.Wall && !map.hasVisited(east)) directions.add(Direction.East);
		if(map.getAt( south ) != CellType.Wall && !map.hasVisited(south)) directions.add(Direction.South);
		if(map.getAt( west ) != CellType.Wall && !map.hasVisited(west)) directions.add(Direction.West);
		return directions;
	}
	public LinkedList<ActionType> getAvailableActions(Map map)
	{
		LinkedList<ActionType> actions = new LinkedList<ActionType>();
		Position north = new Position( 0, 1).add(_position);
		Position east = new Position ( 1, 0).add(_position);
		Position south = new Position( 0,-1).add(_position);
		Position west = new Position (-1, 0).add(_position);
		
		Position forward;
		switch(_direction)
		{
			case North:
				forward = north; break;
			case East:
				forward = east; break;
			case South:
				forward = south; break;
			default:
				forward = west; break;
		}
		
		if(map.getAt( forward ) != CellType.Wall && !map.hasVisited(forward)) actions.add(ActionType.Go);
		actions.add(ActionType.TurnLeft);
		actions.add(ActionType.TurnRight);
		return actions;
	}
	public static String actionTypeToString(ActionType action)
	{
		if(action == ActionType.TurnOn) return "TURN_ON";
		if(action == ActionType.TurnOff) return "TURN_OFF";
		if(action == ActionType.Go) return "GO";
		if(action == ActionType.TurnLeft) return "TURN_LEFT";
		if(action == ActionType.TurnRight) return "TURN_RIGHT";
		if(action == ActionType.Suck) return "SUCK";
		return null;
	}
	private static Direction rotateDirectionCW(Direction dir)
	{
		if(dir == Direction.North) return Direction.East;
		if(dir == Direction.East) return Direction.South;
		if(dir == Direction.South) return Direction.West;
		return Direction.North;
	}
	private static Direction rotateDirectionCCW(Direction dir)
	{
		if(dir == Direction.North) return Direction.West;
		if(dir == Direction.East) return Direction.North;
		if(dir == Direction.South) return Direction.East;
		return Direction.South;
	}
}
